package com.roadmap.client.module;

import java.util.Date;
import java.util.Hashtable;
import java.util.List;

import org.apache.log4j.Logger;

import com.roadmap.biz.message.NoticeManager;
import com.roadmap.common.BaseConst;
import com.roadmap.common.BaseEntity;
import com.roadmap.common.RdcLogger;
import com.roadmap.common.RdcResourceBundle;
import com.roadmap.common.BaseEnum.ActionType;
import com.roadmap.entities.Article;
import com.roadmap.entities.Aspirant;
import com.roadmap.entities.Assessment;
import com.roadmap.entities.Execution;
import com.roadmap.entities.LifeGoal;
import com.roadmap.entities.LifePlan;
import com.roadmap.entities.LifeTask;
import com.roadmap.entities.Milestone;
import com.roadmap.entities.Summary;
import com.roadmap.exception.ActionNotSupportedException;
import com.roadmap.exception.EntityNotFoundException;
import com.roadmap.exception.InvalidActionException;
import com.roadmap.exception.InvalidQueryException;
import com.roadmap.exception.InvalidStatusException;
import com.roadmap.exception.InvalidValueException;
import com.roadmap.exception.NullObjectException;
import com.roadmap.exception.ServerException;
import com.roadmap.repo.db.DatabaseUtility;
import com.roadmap.repo.db.IDatabaseUtility;

public abstract class BaseManager implements IManager {
	private final static Logger logger = RdcLogger.getLogger(BaseManager.class);
	protected static Hashtable<Class, String> _entityIdNames;
	/** whose objects is to be managed */
	private String _ownerId;
	private Aspirant _owner;
	/**
	 * The _parent object has the objects to be managed. E.g. the manager is to
	 * manage goals whose _parent is Aspirant. If the manager is to manage plan
	 * whose _parent should be a goal.
	 */
	private BaseEntity _parent;
	private String _parentId;
	private Class _parentType;
	private final NoticeManager _noticeManager;
	private final IDatabaseUtility _dbUtil = new DatabaseUtility();
	/** what kind of entities is being managed */
	private final Class _managedEntityType;

	protected java.util.Vector<BaseEntity> _entities = new java.util.Vector<BaseEntity>();

	/**
	 * if _parent or _parent type is not specified, the owner will be assigned
	 * to be the owner of the manager
	 */
	public BaseManager(String parentId, Class managedEntityType) {
		this._ownerId = parentId;
		// this._actRecorder = new ActRecorder(_ownerId, _dbUtil);
		this._noticeManager = new NoticeManager(_ownerId, _dbUtil);
		this._managedEntityType = managedEntityType;
		this._parentId = this._ownerId;
		this._parentType = Aspirant.class;
		/*
		 * try { this._parent = createParent(Aspirant.class, parentId); } catch
		 * (Exception e) { logger.error("Initization of manager failed.", e); }
		 */

		initEntityIdNames();
	}

	public BaseManager(String aspirantId, Class managedEntityType,
			Class parentType, String parentId) {
		this._ownerId = aspirantId;
		// this._actRecorder = new ActRecorder(aspirantId, _dbUtil);
		this._noticeManager = new NoticeManager(_ownerId, _dbUtil);
		this._managedEntityType = managedEntityType;
		this._parentId = parentId;
		this._parentType = parentType;
		/*
		 * try { this._parent = createParent(Aspirant.class, parentId); } catch
		 * (Exception e) { logger.error("Initization of manager failed.", e); }
		 */

		initEntityIdNames();
	}

	public BaseManager(String aspirantId, Class managedEntityType,
			BaseEntity parent) {
		this._ownerId = aspirantId;
		// this._actRecorder = new ActRecorder(aspirantId, _dbUtil);
		this._noticeManager = new NoticeManager(_ownerId, _dbUtil);
		this._managedEntityType = managedEntityType;
		this._parent = parent;
		this._parentId = parent.getId();
		this._parentType = parent.getClass();
		initEntityIdNames();
	}

	private void initEntityIdNames() {
		if (_entityIdNames == null) {
			_entityIdNames = new Hashtable<Class, String>();
			_entityIdNames.put(Aspirant.class, "aspirantId");
			_entityIdNames.put(LifeGoal.class, "goalId");
			_entityIdNames.put(LifePlan.class, "planId");
			_entityIdNames.put(LifeTask.class, "taskId");
			_entityIdNames.put(Execution.class, "exeId");
			_entityIdNames.put(Assessment.class, "assessId");
			_entityIdNames.put(Milestone.class, "stoneId");
			_entityIdNames.put(Summary.class, "summaryId");
			_entityIdNames.put(Article.class, "articleId");
		}
	}

	public void cancel(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {
		ActionNotSupportedException ane = new ActionNotSupportedException();
		logger.error("The mamanger doesn't support this action.");
		throw ane;
	}

	/**
	 * check if the entity is a valid child of the manager's _parent
	 * 
	 * @throws InvalidValueException
	 */
	protected boolean checkEntityIsValid(BaseEntity entity)
			throws InvalidValueException {
		if (getManagedEntityType().isInstance(entity)) {
			return true;
		}

		InvalidValueException ive = new InvalidValueException();
		String msg = "The entity is not an instance of :"
				+ getManagedEntityType().getName();
		logger.error(msg, ive);
		throw ive;
	}

	public void complete(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			InvalidStatusException {
		ActionNotSupportedException ane = new ActionNotSupportedException();
		logger.error("The mamanger doesn't support this action.");
		throw ane;
	}

	public void copy(BaseEntity entity) throws ActionNotSupportedException {
		ActionNotSupportedException ane = new ActionNotSupportedException();
		logger.error("The mamanger doesn't support this action.");
		throw ane;
	}

	public void create(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException,
			InvalidStatusException {
		if (checkEntityIsValid(entity)) {
			getDbUtil().save(entity);
			getNoticeManager().notifyForEntry(entity,
					ActionType.CREATE, null);
		}
	}

	public void delete(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException,
			EntityNotFoundException, ActionNotSupportedException {
		if (checkEntityIsValid(entity)) {
			getDbUtil().delete(entity);
		}
	}

	public void edit(BaseEntity entity) throws NullObjectException,
			InvalidActionException, InvalidValueException, ServerException {
		if (checkEntityIsValid(entity)) {
			getDbUtil().update(entity);
		}
	}

	public BaseEntity get(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		return getDbUtil().getEntityById(getManagedEntityType(), entityId);
	}

	public NoticeManager getNoticeManager() {
		return _noticeManager;
	}

	public BaseEntity getParent() {
		if (_parent == null) {
			// if the parent is not initialised, return the owner as its parent
			try {
				this._parent = createParent(_parentType, _parentId);
			}
			catch (Exception e) {
				logger.error("Initization of manager failed.", e);
			}
		}
		return _parent;
	}

	public Aspirant getOwner() throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		if (_owner == null) {
			if (getParent() instanceof Aspirant)
				_owner = (Aspirant) getParent();
			else {
				_owner = (Aspirant) getDbUtil().getEntityById(Aspirant.class,
						getOwnerId());
			}
		}
		return _owner;
	}

	public void setParent(BaseEntity parent) {
		this._parent = parent;
		this._parentId = parent.getId();
		this._parentType = parent.getClass();
	}

	public IDatabaseUtility getDbUtil() {
		return _dbUtil;
	}

	public List getEntitiesByStatus(int[] states)
			throws ActionNotSupportedException, InvalidValueException,
			InvalidQueryException, ServerException {
		ActionNotSupportedException ane = new ActionNotSupportedException();
		logger.error("The mamanger doesn't support this action.");
		throw ane;
	}

	public List getEntitiesInDateScope(Date first, Date last)
			throws ActionNotSupportedException, NullObjectException,
			InvalidActionException, InvalidValueException, ServerException {
		ActionNotSupportedException ane = new ActionNotSupportedException();
		logger.error("The mamanger doesn't support this action.");
		throw ane;
	}

	public List getEntitiesOfAspirant() throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		String criteria = "aspirantId='" + getOwnerId()
				+ "' order by createdTime DESC";
		return getDbUtil().getEntitiesByCriteria(getManagedEntityType(),
				criteria);
	}

	public List getEntitiesOfParent() throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		String criteria = getParentIdName() + "='" + getParent().getId()
				+ "' order by createdTime DESC";
		return getDbUtil().getEntitiesByCriteria(getManagedEntityType(),
				criteria);
	}

	public Class getManagedEntityType() {
		return _managedEntityType;
	}

	public String getOwnerId() {
		return _ownerId;
	}

	protected BaseEntity createParent(Class _parentType, String _parentId) {
		try {
			return getDbUtil().getEntityById(_parentType, _parentId);
		}
		catch (Exception e) {
			logger.error("Failed to create the parent", e);
		}

		return null;
	}

	// get the name of the attribute 'id'
	protected String getParentIdName() throws InvalidValueException {
		if (getParent() == null) {
			InvalidValueException ive = new InvalidValueException();
			logger.error("The manager's parent is invalid!");
			throw ive;
		}

		String name = _entityIdNames.get(getParent().getClass());
		if (name == null) {
			InvalidValueException ive = new InvalidValueException();
			logger.error("The class type is invalid!", ive);
			throw ive;
		}
		return name;
	}

	public void restore(BaseEntity entity, int targetStatus)
			throws ActionNotSupportedException, InvalidValueException,
			InvalidActionException, ServerException, EntityNotFoundException {
		ActionNotSupportedException ane = new ActionNotSupportedException();
		logger.error("The mamanger doesn't support this action.");
		throw ane;
	}

	// actually goals and tasks have status
	protected List<BaseEntity> obtainEntitiesByStatus(int[] states)
			throws ActionNotSupportedException, InvalidValueException,
			InvalidQueryException, ServerException {
		if (states == null || states.length < 1) {
			InvalidValueException ive = new InvalidValueException();
			logger.error("Input is empty.", ive);
			throw ive;
		}

		// when the states contain "ALL" that means all items should be
		// returned, other status will be overlooked
		for (int i = 0; i < states.length; i++) {
			if (states[i] == BaseConst.LIFEGOAL_STATUS_ALL
					|| states[i] == BaseConst.LIFETASK_STATUS_ALL) {
				return getEntitiesOfParent();
			}
		}

		StringBuffer criteria = new StringBuffer(getParentIdName() + "='");
		criteria.append(getParent().getId() + "' and (status=");
		for (int i = 0; i < states.length; i++) {
			criteria.append(states[i]);
			if (i != states.length - 1) {
				criteria.append(" or ");
				criteria.append("status=");
			}
		}
		criteria.append(")");
		return getDbUtil().getEntitiesByCriteria(_managedEntityType,
				criteria.toString());
	}

	public List getExecutableEntities() throws ActionNotSupportedException,
			InvalidQueryException, InvalidValueException, ServerException {
		ActionNotSupportedException ane = new ActionNotSupportedException();
		logger.error("The mamanger doesn't support this action.");
		throw ane;
	}

	public String getEntityHealthy(BaseEntity entity)
			throws ActionNotSupportedException, InvalidValueException {
		return RdcResourceBundle.Entity_Symptom_Healthy;
	}

	public BaseEntity getEntityExt(String entityId) throws NullObjectException,
			InvalidQueryException, ServerException, InvalidValueException {
		return null;
	}
}
